
#include "Weapon.h"
#include "Blaster.h"
#include "../Utils/Timer.h"
#include "../World.h"
#include "../Map.h"
#include "../Explosion.h"
#include "../Graphics/TextureManager.h"

#include <cassert>
#include <GL/gl.h>
#include <iostream>

BlasterBolt::BlasterBolt(Player *parent_player, float speed, unsigned count) : 
    parent_player(parent_player),
    is_active(true) {

    assert(parent_player != NULL);

    position = parent_player->getShip()->getPosition();
    velocity = parent_player->getShip()->getHeading()*speed + parent_player->getShip()->getVelocity();

    fire_direction = parent_player->getShip()->getHeading();
    fire_direction.normalise();

    perp_fire_direction = fire_direction;
    perp_fire_direction.rotate(90.0f);

    // Alternate firing positions of the blasters to either side of the ship.
    if(count%2 == 0){
        position = position + parent_player->getShip()->getSize()*0.15f*perp_fire_direction;
    }
    else{
        position = position - parent_player->getShip()->getSize()*0.15f*perp_fire_direction;
    }

    texture_id = TextureManager::instance().loadTexture("data/Star.png");
}

BlasterBolt::~BlasterBolt(){

}

void BlasterBolt::render(void){
    glPushMatrix();

    Vector2D vec = -1.0f * fire_direction;
    vec.scale(0.2f);

    Vector2D offset = perp_fire_direction;
    offset.scale(0.02f);

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE);
    
    glEnable(GL_TEXTURE_2D);
    TextureManager::instance().bindTexture(texture_id);

    glBegin(GL_QUADS);
      glColor3f(1.0f, 1.0f, 1.0f);
      glTexCoord2f(0.0f, 0.0f);
      glVertex3f(position.x+offset.x, position.y+offset.y, 0.0f);

      glTexCoord2f(0.0f, 1.0f);
      glVertex3f(position.x-offset.x, position.y-offset.y, 0.0f);
      

      glColor3f(0.7f, 0.7f, 0.7f);
      glTexCoord2f(1.0f, 1.0f);
      glVertex3f(position.x+vec.x-0.5f*offset.x, position.y+vec.y-0.5f*offset.y, 0.0f);
      
      glTexCoord2f(1.0f, 0.0f);
      glVertex3f(position.x+vec.x+0.5f*offset.x, position.y+vec.y+0.5f*offset.y, 0.0f);
    glEnd();

    glDisable(GL_TEXTURE_2D);
    glDisable(GL_BLEND);

    glPopMatrix();
}

void BlasterBolt::update(void){
    prev_position = position;
    position.add(velocity * Timer::secondsSinceLastFrame()); 
}

bool BlasterBolt::active(void){
    return is_active;
}

bool BlasterBolt::resolveCollision(Ship *ship){
    assert(ship != NULL);

    if(ship != parent_player->getShip() && 
      (ship->getPosition()-position).length() < ship->getSize()){
     
        Vector2D normal = position - ship->getPosition();
        normal.normalise();

        Vector2D epos = ship->getPosition()+ship->getSize()*normal;
        Explosion *e = new Explosion(50, epos, 0.5f*normal, 
                                     0.0f, 0.5f,
                                     1.0f, 0.5f, 0.0f, 
                                     0.07f, 0.2f, "data/Spark.png");
        World::instance().addObject(e);

        // Bump the ship. TODO: make bump amount settable/variable
        //Vector2D ship_v = ship->getVelocity();
        //ship_v.add(0.5f*velocity);
        //ship->setVelocity(ship_v);

        // Deal damage to the players ship.
        ship->dealDamage(10); // TODO: make damage a setting for this weapon
        ship->impact(epos);

        is_active = false;
        parent_player->incShotsHit();
        return true;
    }
 
    return false;
}

bool BlasterBolt::resolveCollision(Weapon *weapon){
    return false;
}

bool BlasterBolt::resolveCollision(Map *map){
    Vector2D cp_normal, cp;

    if(map->collideRaySegment(prev_position, position, cp_normal, cp)){
        Vector2D ebias = velocity;
        ebias.normalise();
        ebias.add(2.0f*cp_normal);
        ebias.normalise();
   
        ebias.scale(0.5f);
 
        Explosion *e = new Explosion(50, cp, ebias, 
                                     0.0f, 0.5f,
                                     1.0f, 0.5f, 0.0f, 
                                     0.07f, 0.2f, "data/Spark.png");
        World::instance().addObject(e);

        is_active = false;
        return true;
    }

    if(!map->pointInsideMap(position)){
        is_active = false;
    }    

    return false;
}

Blaster::Blaster(int ammo_count, int lifetime, Ship *pship) :
    WeaponGenerator(ammo_count, lifetime, 0.2f, pship),
    bolt_sound(Sound::instance().load("data/Sounds/blaster1.mp3")),
    num_bolts_fired(0) {

    assert(parent_ship != NULL);
}

Blaster::~Blaster(){

}

bool Blaster::shouldDiscard(void){
    // Has the weapon expired because it has been picked up too long ago?
    if(lifetime > 0 && (spawn_time + lifetime*1000) > Timer::currentTicks()){
        return true;
    }
    // Has the weapon expired because its ammo count is exhausted?
    else if(ammo_count == 0){
        return true;
    }
    else{
        return false;
    }
}

bool Blaster::fire(void) {
    // Figure out if we CAN fire first
    if(ammo_count != 0 && // have we run out of ammo?
      (Timer::currentTicks() - time_of_last_fire)/1000.0f >= rate_of_fire && // did we fire too recently?
      parent_ship->getEnergy() > 10){

        time_of_last_fire = Timer::currentTicks();
        ammo_count--;

        World::instance().addObject(new BlasterBolt(parent_ship->getControllingPlayer(), 7.0f, num_bolts_fired));
        Sound::instance().play(bolt_sound);
        num_bolts_fired++;

        parent_ship->getControllingPlayer()->incShotsFired();
        parent_ship->drainEnergy(10);
        return true;
    }
    else{
        return false;
    }
}

